package com.doupi.partner.manager.gateway.wx;

import com.doupi.partner.util.SpringContextUtils;
import org.apache.commons.lang3.RandomStringUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.HttpEntity;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.conn.ssl.DefaultHostnameVerifier;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.ssl.SSLContexts;
import org.apache.http.util.EntityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.net.ssl.SSLContext;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.security.*;
import java.util.HashMap;
import java.util.Map;

/**
 * 微信支付工具
 * 
 * @author Super Jens Wang
 *
 * @version 1.0 @ 2016年10月24日 下午11:55:57
 */
public class PayUtils {
	private final static Logger LOGGER = LoggerFactory.getLogger(PayUtils.class);

	/**
	 * 随机字符串集合
	 */
	private final static String LETTER_AND_NUMBER = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";

	/**
	 * 连接超时时间，默认10秒
	 */
	private final static int SOCKET_TIMEOUT = 10000;

	/**
	 * 传输超时时间，默认30秒
	 */
	private final static int CONNECT_TIMEOUT = 30000;

	/**
	 * 请求器的配置
	 */
	private static RequestConfig requestConfig = null;

	/**
	 * HTTP请求器
	 */
	private static CloseableHttpClient httpClient = null;

	/**
	 * 是否测试
	 */
	private static Boolean debug = null;

	/**
	 * 下单
	 * 
	 * https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_1
	 * 
	 * <pre>
	 * 接口链接
	 * URL地址：https://api.mch.weixin.qq.com/pay/unifiedorder
	 * 是否需要证书
	 * 否
	 * </pre>
	 */
	public static Map<String, String> unifiedorder(Map<String, String> maps) {
		String url = "https://api.mch.weixin.qq.com/pay/unifiedorder";
		if (isDebug()) {
			url = "https://api.mch.weixin.qq.com/sandbox/pay/unifiedorder";
		}
		initPublic(maps);
		Map<String, String> resultMaps = null;
		try {
			String response = sendPost(url, maps);
			resultMaps = XmlUtils.getMapFromXML(response);
		} catch (Exception e) {
			LOGGER.error("发送请求失败", e);
		}
		return resultMaps;
	}

	/**
	 * 查询订单
	 * 
	 * https://pay.weixin.qq.com/wiki/doc/api/micropay.php?chapter=9_2
	 * 
	 * <pre>
	 * 接口链接
	 * https://api.mch.weixin.qq.com/pay/orderquery
	 * 是否需要证书
	 * 不需要
	 * </pre>
	 */
	public static Map<String, String> orderquery(Map<String, String> maps) {
		String url = "https://api.mch.weixin.qq.com/pay/orderquery";
		if (isDebug()) {
			url = "https://api.mch.weixin.qq.com/sandbox/pay/orderquery";
		}
		initPublic(maps);
		Map<String, String> resultMaps = null;
		try {
			String response = sendPost(url, maps);
			resultMaps = XmlUtils.getMapFromXML(response);
		} catch (Exception e) {
			LOGGER.error("发送请求失败", e);
		}
		return resultMaps;
	}

	/**
	 * 申请退款
	 * 
	 * https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_4
	 * 
	 * <pre>
	 * 接口链接
	 * 接口链接：https://api.mch.weixin.qq.com/secapi/pay/refund
	 * 是否需要证书
	 * 请求需要双向证书。 详见证书使用
	 * </pre>
	 * 
	 */
	public static Map<String, String> refund(Map<String, String> maps) {
		String url = "https://api.mch.weixin.qq.com/secapi/pay/refund";
		if (isDebug()) {
			url = "https://api.mch.weixin.qq.com/sandbox/pay/refund";
		}
		WxUtils wxUtils = SpringContextUtils.getBeanByClass (WxUtils.class);
		maps.put("appid", wxUtils.getAppId());
		maps.put("mch_id", wxUtils.getMchId());
		maps.put("nonce_str", RandomStringUtils.random(32, LETTER_AND_NUMBER));
		String sign = Signature.getSign(maps);
		maps.put("sign", sign);
		Map<String, String> resultMaps = null;
		try {
			String response = sendPost(url, maps);
			resultMaps = XmlUtils.getMapFromXML(response);
		} catch (Exception e) {
			LOGGER.error("发送请求失败", e);
		}
		return resultMaps;
	}

	/**
	 * 根据订单号查询微信订单
	 */
	public static Map<String, String> orderquery(String outTradeNo) {
		Map<String, String> maps = new HashMap<String, String>();
		maps.put("out_trade_no", outTradeNo);
		return orderquery(maps);
	}

	/**
	 * 企业向个人付款 提现
	 * 
	 * https://pay.weixin.qq.com/wiki/doc/api/tools/mch_pay.php?chapter=14_2
	 * 
	 * <pre>
	 * 接口地址
	 * 接口链接：https://api.mch.weixin.qq.com/mmpaymkttransfers/promotion/transfers
	 * 是否需要证书
	 * 请求需要双向证书。 详见证书使用
	 * </pre>
	 */
	public static Map<String, String> transfers(Map<String, String> maps) {
		String url = "https://api.mch.weixin.qq.com/mmpaymkttransfers/promotion/transfers";
		if (isDebug()) {
			url = "https://api.mch.weixin.qq.com/sandbox/mmpaymkttransfers/promotion/transfers";
		}
		WxUtils wxUtils = SpringContextUtils.getBeanByClass (WxUtils.class);
		maps.put("mch_appid", wxUtils.getAppId());
		maps.put("mchid", wxUtils.getMchId());
		maps.put("nonce_str", RandomStringUtils.random(32, LETTER_AND_NUMBER));
		maps.put("check_name", "NO_CHECK");
		maps.put("desc", "象左象右");
		maps.put("spbill_create_ip", "139.196.21.104");
		String sign = Signature.getSign(maps);
		maps.put("sign", sign);
		Map<String, String> resultMaps = null;
		try {
			String response = sendPost(url, maps);
			resultMaps = XmlUtils.getMapFromXML(response);
		} catch (Exception e) {
			LOGGER.error("发送请求失败", e);
		}
		return resultMaps;
	}

	/**
	 * 根据订单号，openid，金额发起提现
	 */
	public static Map<String, String> transfers(String partnerTradeNo, String openid, String amount) {
		Map<String, String> maps = new HashMap<String, String>();
		maps.put("partner_trade_no", partnerTradeNo);
		maps.put("openid", openid);
		maps.put("amount", amount);
		return transfers(maps);
	}

	/**
	 * 查询企业付款信息
	 * 
	 * https://pay.weixin.qq.com/wiki/doc/api/tools/mch_pay.php?chapter=14_3
	 * 
	 * <pre>
	 * 请求Url	https://api.mch.weixin.qq.com/mmpaymkttransfers/gettransferinfo
	 * 是否需要证书	请求需要双向证书。 详见证书使用
	 * 请求方式	POST
	 * </pre>
	 */
	public static Map<String, String> gettransferinfo(Map<String, String> maps) {
		String url = "https://api.mch.weixin.qq.com/mmpaymkttransfers/gettransferinfo";
		if (isDebug()) {
			url = "https://api.mch.weixin.qq.com/sandbox/mmpaymkttransfers/gettransferinfo";
		}
		initPublic(maps);
		String sign = Signature.getSign(maps);
		maps.put("sign", sign);
		Map<String, String> resultMaps = null;
		try {
			String response = sendPost(url, maps);
			resultMaps = XmlUtils.getMapFromXML(response);
		} catch (Exception e) {
			LOGGER.error("发送请求失败", e);
		}
		return resultMaps;
	}

	/**
	 * 根据商户订单号查询商户付款信息
	 */
	public static Map<String, String> gettransferinfo(String partnerTradeNo) {
		Map<String, String> maps = new HashMap<String, String>();
		maps.put("partner_trade_no", partnerTradeNo);
		return gettransferinfo(maps);
	}

	/**
	 * 通过Https往API post xml数据
	 *
	 * @param url API地址
	 * @param xmlObj 要提交的XML数据对象
	 * @return API回包的实际数据
	 * @throws IOException
	 * @throws KeyStoreException
	 * @throws UnrecoverableKeyException
	 * @throws NoSuchAlgorithmException
	 * @throws KeyManagementException
	 */
	public static String sendPost(String url, Map<String, String> xmlObj) throws IOException, KeyStoreException, UnrecoverableKeyException, NoSuchAlgorithmException, KeyManagementException {
		if (null == httpClient) {
			initHttClient();
		}

		String result = null;

		HttpPost httpPost = new HttpPost(url);

		// 解决XStream对出现双下划线的bug
		// XStream xStreamForRequestPostData = new XStream(new DomDriver("UTF-8", new XmlFriendlyNameCoder("-_", "_")));

		// 将要提交给API的数据对象转换成XML格式数据Post给API
		// String postDataXML = xStreamForRequestPostData.toXML(xmlObj);
		String postDataXML = XmlUtils.createXML(xmlObj);

		LOGGER.info("API，POST过去的数据是{}", postDataXML);

		// 得指明使用UTF-8编码，否则到API服务器XML的中文不能被成功识别
		StringEntity postEntity = new StringEntity(postDataXML, "UTF-8");
		httpPost.addHeader("Content-Type", "application/xml");
		httpPost.setEntity(postEntity);

		// 设置请求器的配置
		httpPost.setConfig(requestConfig);

		LOGGER.info("开始执行请求{}", httpPost.getRequestLine());

		try {
			CloseableHttpResponse response = httpClient.execute(httpPost);
			try {
				HttpEntity entity = response.getEntity();
				result = EntityUtils.toString(entity, "UTF-8");
				EntityUtils.consume(entity);
			} finally {
				response.close();
			}
		} catch (Exception e) {
			LOGGER.error("http get throw Exception", e);
		} finally {
			httpPost.abort();
		}
		LOGGER.info("微信响应信息{}", result);
		return result;
	}

	/**
	 * 结果是否成功
	 */
	public static boolean isAllSuccess(Map<String, String> maps) {
		return null != maps && StringUtils.equals("SUCCESS", maps.get("return_code")) && StringUtils.equals("SUCCESS", maps.get("result_code"));
	}

	/**
	 * 是否调用成功
	 */
	public static boolean isSendSuccess(Map<String, String> maps) {
		return null != maps && StringUtils.equals("SUCCESS", maps.get("return_code"));
	}

	/**
	 * 是否交易成功
	 */
	public static boolean isDealSuccess(Map<String, String> maps) {
		return null != maps && StringUtils.equals("SUCCESS", maps.get("result_code"));
	}

	/**
	 * 获取nonce_str
	 */
	public static String getNonceStr() {
		return RandomStringUtils.random(32, LETTER_AND_NUMBER);
	}

	public static boolean isDebug() {
		if (null == debug) {
			debug =  SpringContextUtils.getBeanByClass (WxUtils.class).getPayDebug();
		}
		return debug;
	}

	/**
	 * 设置公共参数值
	 */
	private static void initPublic(Map<String, String> maps) {
		WxUtils wxUtils = SpringContextUtils.getBeanByClass (WxUtils.class);
		maps.put("appid", wxUtils.getAppId());
		maps.put("mch_id", wxUtils.getMchId());
		maps.put("nonce_str", getNonceStr());
		String sign = Signature.getSign(maps);
		maps.put("sign", sign);
	}

	/**
	 * 初始微信链接
	 */
	private static void initHttClient() throws IOException, KeyStoreException, UnrecoverableKeyException, NoSuchAlgorithmException, KeyManagementException {
		WxUtils wxUtils = SpringContextUtils.getBeanByClass (WxUtils.class);
		KeyStore keyStore = KeyStore.getInstance("PKCS12");
		FileInputStream instream = new FileInputStream(new File(wxUtils.getCertLocalPath()));// 加载本地的证书进行https加密传输

		boolean errorFlag = false;

		try {
			keyStore.load(instream, wxUtils.getCertPassword().toCharArray());// 设置证书密码
		} catch (Exception e) {
			LOGGER.error("获取证书失败", e);
			errorFlag = true;
		} finally {
			instream.close();
		}

		if (errorFlag) {
			LOGGER.warn("证书加载失败，请检查证书是否正确");
			return;
		}

		// Trust own CA and all self-signed certs
		SSLContext sslcontext = SSLContexts.custom().loadKeyMaterial(keyStore, wxUtils.getCertPassword().toCharArray()).build();
		// Allow TLSv1 protocol only
		// SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslcontext, new String[] { "TLSv1" }, null,
		// SSLConnectionSocketFactory.BROWSER_COMPATIBLE_HOSTNAME_VERIFIER);
		SSLConnectionSocketFactory sslConnectionSocketFactory = new SSLConnectionSocketFactory(sslcontext, new String[] { "TLSv1" }, null, new DefaultHostnameVerifier());
		httpClient = HttpClients.custom().setSSLSocketFactory(sslConnectionSocketFactory).build();

		// 根据默认超时限制初始化requestConfig
		requestConfig = RequestConfig.custom().setSocketTimeout(SOCKET_TIMEOUT).setConnectTimeout(CONNECT_TIMEOUT).build();
	}
}
